﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Qas.CommonLogic.BusinessObjects.Probability;
using Qas.CommonLogic.BusinessObjects;

namespace Qas.CommonLogic.BusinessObjects
{
    /// <summary>
    /// Задача представляет собой чёрный ящик, который переводит 
    /// набор предусловий в набор постусловий. Задача однозначно 
    /// характеризуется двумя наборами условий (пред- и пост- условия)
    /// </summary>
    public partial class Task : AbstractObject
    {

        private List<Condition> preCond;
        private List<Condition> postCond;
        private List<Competence> requiredCompetences;

        private ProbabilityDistribution pdTime = null;
        private ProbabilityDistribution pdCost = null;

        private double taskValue = 0;
        private double taskAccessibility = -1;

        /// <summary>
        /// Достижимость задачи в контексте предусловий
        /// </summary>
        [System.Xml.Serialization.XmlIgnore()]
        public double Accessibility
        {
            get
            {
                return taskAccessibility;
            }
        }

        /// <summary>
        /// Ценность задачи в контексте целей
        /// </summary>
        [System.Xml.Serialization.XmlIgnore()]
        public double Value
        {
            get
            {
                if (this.taskValue == -1)
                {
                    // Задача ещё не оценена
                }

                return this.taskValue;
            }
            set
            {
                this.taskValue = value;
            }
        }        

        /// <summary>
        /// Оценка задачи - расчёт её ценности и достижимости
        /// </summary>        
        public void Evaluate(List<Condition> initials, List<Condition> goals)
        {
            this.taskValue = 0;
            foreach (Condition gCondition in goals)
            {
                if (this.PostConditions.Contains(gCondition))
                {
                    // Задача продуцирует рассматриваемое условие
                    this.taskValue += 1 / gCondition.Accessibility;
                }

                if (double.IsInfinity(this.taskValue))
                {
                    Console.WriteLine(this.Title);
                }
            }

            this.taskAccessibility = -1;
            foreach (Condition iCondition in initials)
            {
                if (this.PreConditions.Contains(iCondition))
                {
                    if (iCondition.Accessibility == 0)
                    {
                        // Задача недостижима
                        this.taskAccessibility = -1;

                        return;
                    }

                    if (this.taskAccessibility == -1)
                    {
                        this.taskAccessibility = 0;
                    }                    

                    // Задача продуцирует рассматриваемое условие
                    this.taskAccessibility += 1 / iCondition.Accessibility;
                }
            }
        }

        /// <summary>
        /// Список предусловий данной задачи
        /// </summary>
        [System.Xml.Serialization.XmlIgnore()]
        public List<Condition> PreConditions
        {
            get
            {
                if (this.preCond == null)
                {
                    // У задачи может быть пустой список предусловий
                    if (this.startConditions != null)
                    {
                        this.preCond = this.startConditions.ToList<Condition>();
                    }
                    else
                    {
                        this.preCond = new List<Condition>();
                    }
                }
                return this.preCond;
            }
        }

        /// <summary>
        /// Список компетенций, необходимых для выполнения данной задачи
        /// </summary>
        [System.Xml.Serialization.XmlIgnore()]
        public List<Competence> RequiredCompetences
        {
            get
            {
                if (this.requiredCompetences == null)
                {
                    // Десериализованный массив комптенций сам по себе может быть пустым
                    if (this.reqCompetences != null)
                    {
                        this.requiredCompetences = this.reqCompetences.ToList<Competence>();
                    }
                    else
                    {
                        this.requiredCompetences = new List<Competence>();
                    }
                }
                return this.requiredCompetences;
            }
        }

        /// <summary>
        /// Продолжительность выполнения задачи
        /// </summary>
        [System.Xml.Serialization.XmlIgnore()]
        public ProbabilityDistribution PDTime
        {
            get
            {
                // Если ранее не производился расчёт продолжительности
                if (this.pdTime == null)
                {
                    this.pdTime = new ProbabilityDistribution(this.Time);
                }

                return this.pdTime;
            }
        }

        /// <summary>
        /// Стоимость выполнения задачи
        /// </summary>
        [System.Xml.Serialization.XmlIgnore()]
        public ProbabilityDistribution PDCost
        {
            get
            {
                // Если ранее не производился расчёт стоимости
                if (this.pdCost == null)
                {
                    this.pdCost = new ProbabilityDistribution(this.Cost);
                }

                return this.pdCost;
            }
        }

        /// <summary>
        /// Список постусловий данной задачи
        /// </summary>
        [System.Xml.Serialization.XmlIgnore()]
        public List<Condition> PostConditions
        {
            get
            {
                if (this.postCond == null)
                {
                    this.postCond = this.finishConditions.ToList<Condition>();
                }
                return this.postCond;
            }
        }

<<<<<<< .mine
        /// <summary>
        /// Добавление нового измерения стоимости
        /// </summary>
        public void AddNewCost(double newCost)
        {
            this.PDCost.AddResult(newCost);
            this.Time = this.PDCost.PairsArray;
        }
=======
        /// <summary>
        /// итоговый коэффициент для задачи на основе коэфиициентов времени, стоимости
        /// и приоритета пользователя
        /// </summary>
        public double Coeff(double budget, double time)
        {
            //return ((this.costCoeff(budget) + this.timeCoeff(time)) /
            //    (this.costCoeff(budget) * this.timeCoeff(time)));
            //return ((this.costCoeff(1) * userValue + this.timeCoeff(1) * (2 - userValue)) /
            //    (this.costCoeff(1) + this.timeCoeff(1)));
            return (this.PDCost.Expectancy * this.PDTime.Expectancy);
        }
>>>>>>> .r230


        /// <summary>
        /// Добавление нового измерения времени
        /// TODO: Обобщить через отражение до каждого параметра
        /// </summary>
        public void AddNewTime(double newTime)
        {
            this.PDTime.AddResult(newTime);
            this.Cost = this.PDTime.PairsArray;

            //for (int i = 0; i < this.time.Length; i++)
            //{
            //    if (this.PDTime.PairsList[i].Value == newTime)
            //    {
            //        valueNum = i;
            //        break;
            //    }
            //}

            //if (valueNum == 0)
            //{
            //    Pair newPair = new Pair(1, newTime);
            //    this.PDTime.PairsList.Add(newPair);

            //    Pair[] newArr = new Pair[this.time.GetLength(0) + 1];
            //    for (int i = 0; i < newArr.GetLength(0) - 1; i++)
            //    {
            //        newArr[i] = this.time[i];
            //    }
            //    newArr[newArr.GetLength(0) - 1] = newPair;

            //    this.time = newArr;
            //}
            //else
            //{
            //    this.time[valueNum].Count++;
            //}
        }

        /// <summary>
        /// Вычитание наборов задач: аналог операции вычетания множеств
        /// </summary>
        /// <param name="firstList">Уменьшаемое</param>
        /// <param name="secondList">Вычитаемое</param>
        /// <returns>Новый набор задач, являющийся результатом вычитания</returns>
        public static Plan Subtraction(Plan firstPlan, Plan secondPlan)
        {
            // Делаем слепок уменьшаемого
            Plan result = new Plan();

            foreach (Task task in firstPlan.Tasks)
            {
                result.AddTask(task);
            }

            foreach (Task task in secondPlan.Tasks)
            {
                // Удаляем из результирующего множества все элементы, 
                // присутствующие как в уменьшаемом, так и в вычитаемом
                if (result.Tasks.Contains(task))
                {
                    result.Tasks.Remove(task);
                }
            }
            return result;
        }


        ///// <summary>
        ///// коэффициент стоимости для задачи
        ///// </summary>
        //private double costCoeff(double plannedCost)
        //{
        //    if (plannedCost == 0)
        //    {
        //        return 0;
        //    }
        //    else
        //    {
        //        return (this.PDCost.Expectancy / plannedCost);
        //    }
        //}


        ///// <summary>
        ///// коэффициент времени для задачи
        ///// </summary>
        //private double timeCoeff(double plannedTime)
        //{
        //    if (plannedTime == 0)
        //    {
        //        return 0;
        //    }
        //    else
        //    {
        //        return (this.PDTime.Expectancy / plannedTime);
        //    }

        //}


        /// <summary>
        /// итоговый коэффициент для задачи на основе коэфиициентов времени, стоимости
        /// и приоритета пользователя
        /// </summary>
        public double Coeff(double userValue)
        {
            //return ((this.costCoeff(budget) + this.timeCoeff(time)) /
            //    (this.costCoeff(budget) * this.timeCoeff(time)));
            if (userValue == 1)
            {
                return (this.PDCost.Expectancy * this.PDTime.Expectancy);
            }
            else
            {
                return ((this.PDCost.Expectancy * (2-userValue) + this.PDTime.Expectancy * userValue) /
                    (this.PDTime.Expectancy + this.PDCost.Expectancy));
            }
        }

        protected bool Contains(List<Condition> first, List<Condition> second)
        {
            foreach (Condition cond in first)
            {
                if (!second.Contains(cond))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// Суммирование наборов задач: аналог операции объединения множеств
        /// </summary>
        /// <param name="firstList">Первый список</param>
        /// <param name="secondList">Второй список</param>
        /// <returns>Новый набор задач, являющийся объединением наборов-слагаемых</returns>
        public static List<Task> Summation(List<Task> firstList, Task task)
        {
            // Создаём слепок первого слагаемого
            List<Task> result = new List<Task>();
            if (firstList != null)
            {
                result.AddRange(firstList);
            }
            result.Add(task);
            
            return result;
        }

        public override string ToString()
        {
            return this.ID.ToString();
        }
    }
}
